Skip to content

10 fix/microservicio clases#11

Closed
RogerCll06 wants to merge 6 commits intomainfrom
10-fix/microservicio-clases
Closed

10 fix/microservicio clases#11
RogerCll06 wants to merge 6 commits intomainfrom
10-fix/microservicio-clases

Conversation

@RogerCll06
Copy link
Copy Markdown
Contributor

@RogerCll06 RogerCll06 commented Oct 11, 2025

Summary by CodeRabbit

  • Nuevas funcionalidades

    • API de Clases: crear, listar, actualizar y eliminar; nuevos DTOs de request/response y mapeos asociados.
  • Mejoras

    • Locations: request/response DTOs, actualización y findAll con paginación, búsqueda y filtro por activo.
    • Manejo de errores: respuestas 403 (acceso denegado) y 404 (no encontrado).
    • Límite de subida: 10MB por archivo y 20MB por petición.
    • Formato de fechas: dd-MM-yyyy en respuestas.
  • Seguridad

    • Ruta pública unificada bajo /test/saludo.
    • Autorización aplicada en endpoints (roles/propietario) y bloqueo de borrado cuando existen relaciones.
  • Cambios

    • Se elimina la interfaz de Swagger UI y se actualiza el nombre del proyecto.

@RogerCll06 RogerCll06 linked an issue Oct 11, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 11, 2025

Walkthrough

Se renombra el artefacto a msvc-classes y se eliminan dependencias de springdoc. Se añaden CRUD de clases, nuevos DTOs y mapeadores, repositorio y servicio para clases. Se reestructura Location y Trainer con DTOs request/response, filtros/paginación y reglas de borrado. Se introduce AuthorizationService(+impl) y ampliación de paths públicos (/test/**). Se ajustan paquetes de enums/DTOs y manejo global de excepciones. Configuración multipart añadida.

Changes

Cohorte / Archivo(s) Resumen
Build
pom.xml
Renombrado del proyecto a msvc-classes; eliminado springdoc-openapi-starter-webmvc-ui duplicado; formateo menor.
Security & Exceptions
src/main/java/com/classes/config/SecurityConfig.java, src/main/java/com/classes/exceptions/GlobalExceptionController.java
Seguridad: añade /test/** a rutas permitidas. Excepciones: handlers activos para AccessDeniedException (403) y EntityNotFoundException (404).
Controllers
src/main/java/com/classes/controllers/ClassController.java, .../controllers/LocationController.java, .../controllers/TrainerController.java, .../controllers/HelloController.java
Nuevo ClassController con CRUD y @PreAuthorize. LocationController migrado a Request/Response, añade filtros/paginación y autorizaciones. TrainerController añade checks de autorización y uso de Authentication. HelloController cambia base path a /test y devuelve ResponseEntity.
DTOs - Class / Location / Trainer
src/main/java/com/classes/dtos/Class/ClassRequest.java, .../Class/ClassResponse.java, .../Location/LocationRequest.java, .../Location/LocationResponse.java, .../Trainer/TrainerDTO.java, .../Trainer/FileResponseDTO.java
Nuevos DTOs para Class y Location (request/response). TrainerDTO y FileResponseDTO movidos al paquete com.classes.dtos.Trainer; formatos de fecha cambiados a dd-MM-yyyy.
Entities
src/main/java/com/classes/entities/ClassEntity.java, .../entities/TrainerEntity.java
ClassEntity: renombrado titlename, añade campos de tiempo/duración/capacidad/active y relaciones @ManyToOne a LocationEntity y TrainerEntity; elimina FK primitivas. TrainerEntity: añade @Embedded Audit y ajusta imports de enums.
Enums
src/main/java/com/classes/enums/*
Cambio de paquete com.classes.Enumscom.classes.enums para ContractType, DayAvailability, Gender, TrainerStatus.
Mappers
src/main/java/com/classes/mappers/ClassMapper.java, .../LocationMapper.java, .../TrainerMapper.java
Nuevo ClassMapper (MapStruct) para entity↔DTO; LocationMapper adaptado a Request/Response y partial update; TrainerMapper ajusta import de TrainerDTO.
Repositories
src/main/java/com/classes/repositories/ClassRepository.java, .../LocationRepository.java, .../TrainerRepository.java
Nuevo ClassRepository; LocationRepository añade consultas paginadas y por estado (active); TrainerRepository añade import Optional.
Services - Interfaces
src/main/java/com/classes/services/AuthorizationService.java, .../ClassService.java, .../LocationService.java, .../TrainerService.java, .../AzureService.java, .../CloudinaryService.java
Nuevas interfaces AuthorizationService y ClassService. LocationService migrado a Request/Response y añade filtros/paginación. Ajustes de import/retorno para FileResponseDTO en servicios de almacenamiento.
Services - Implementaciones
src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java, .../ClassServiceImpl.java, .../LocationServiceImpl.java, .../TrainerServiceImpl.java, .../AzureServiceImpl.java, .../CloudinaryServiceImpl.java, eliminado src/main/java/com/classes/services/TrainerServiceImpl.java
Nueva impl. AuthorizationServiceImpl. Nueva ClassServiceImpl. LocationServiceImpl adaptado a Request/Response, filtros y bloqueo de borrado si hay clases. TrainerServiceImpl (impl) amplía manejo de ficheros, transacciones y evita borrado si tiene clases. Se elimina una clase vacía services/TrainerServiceImpl.java. Importes de FileResponseDTO realineados.
Config
src/main/resources/bootstrap.yml
Añadida configuración multipart: spring.servlet.multipart.max-file-size: 10MB, max-request-size: 20MB.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Controller as ClassController
  participant Service as ClassServiceImpl
  participant RepoT as TrainerRepository
  participant RepoL as LocationRepository
  participant Mapper as ClassMapper
  participant RepoC as ClassRepository

  Client->>Controller: POST /api/classes (ClassRequest)
  Controller->>Service: createClass(request)
  Service->>RepoT: findById(request.trainerId)
  RepoT-->>Service: TrainerEntity | not found
  Service->>RepoL: findById(request.locationId)
  RepoL-->>Service: LocationEntity | not found
  Service->>Mapper: toEntity(request)
  Mapper-->>Service: ClassEntity
  Service->>Service: set trainer & location
  Service->>RepoC: save(entity)
  RepoC-->>Service: ClassEntity
  Service->>Mapper: toResponse(entity)
  Mapper-->>Service: ClassResponse
  Service-->>Controller: ClassResponse
  Controller-->>Client: 201 Created (ClassResponse)
Loading
sequenceDiagram
  autonumber
  actor Client
  participant Controller as LocationController
  participant Service as LocationServiceImpl
  participant Repo as LocationRepository
  participant Mapper as LocationMapper

  Client->>Controller: GET /api/locations?page=&size=&search=&active=
  Controller->>Service: findAll(page,size,search,active)
  alt search && active!=null
    Service->>Repo: findByNameContainingIgnoreCaseAndActive(search, active, pageable)
  else search
    Service->>Repo: findByNameContainingIgnoreCase(search, pageable)
  else active==true
    Service->>Repo: findByActiveTrue(pageable)
  else active==false
    Service->>Repo: findByActiveFalse(pageable)
  else none
    Service->>Repo: findAll(pageable)
  end
  Repo-->>Service: Page<LocationEntity>
  Service->>Mapper: map each to Response
  Mapper-->>Service: Page<LocationResponse>
  Service-->>Controller: Page<LocationResponse>
  Controller-->>Client: 200 OK (Page<LocationResponse>)
Loading
sequenceDiagram
  autonumber
  actor Client
  participant Controller as TrainerController
  participant Authz as AuthorizationServiceImpl
  participant Service as TrainerServiceImpl
  participant ClassRepo as ClassRepository

  Client->>Controller: DELETE /api/trainers/{id}
  Controller->>Authz: canAccessResource(id, authentication)
  alt allowed
    Controller->>Service: deleteTrainer(id)
    Service->>ClassRepo: findFirstByTrainerId(id)
    alt has classes
      Service-->>Controller: throw IllegalArgumentException
      Controller-->>Client: 400/409 Error
    else none
      Service-->>Controller: void
      Controller-->>Client: 200 OK
    end
  else denied
    Controller-->>Client: 403 Forbidden
  end
Loading

Estimated code review effort

🎯 4 (Complejo) | ⏱️ ~60 minutos

Possibly related PRs

  • Dev #5: Coincidencias directas en cambios de SecurityConfig, HelloController y reestructuración de Location — posible solapamiento de rutas/seguridad y DTOs.
  • Dev #8: Cambios concurrentes en enums, FileResponseDTO y servicios de Azure/Cloudinary; alto potencial de conflicto en imports y tipos DTO.

Poem

Salté de madriguera con un DTO en la mano,
cambié nombres y rutas, y ordené el hermano. 🐇
Con autorización y mapeos canto ligero,
/test me responde: "Microservicio sincero".
Si hay clases ligadas, cuido el borrado certero.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning El título “10 fix/microservicio clases” no resume de forma clara las modificaciones principales de la PR y parece referirse al nombre de la rama en lugar de describir el cambio de renombrar el proyecto y ajustes de endpoints y dependencias. Renombra el título para reflejar el cambio principal, por ejemplo “Renombrar proyecto a msvc-classes, eliminar dependencia springdoc duplicada y exponer /test públicamente sin autenticación”.
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 10-fix/microservicio-clases

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/com/classes/enums/TrainerStatus.java (1)

5-5: Corregir error tipográfico en el enum.

El valor del enum INACTIVo tiene una 'o' minúscula en lugar de una 'O' mayúscula. Esto puede causar inconsistencias y errores en el código que espera el valor INACTIVO en mayúsculas completas.

Aplique este diff para corregir el error:

-    INACTIVo,       // No está disponible actualmente
+    INACTIVO,       // No está disponible actualmente
src/main/java/com/classes/controllers/TrainerController.java (1)

36-64: Corregir expresión @PreAuthorize inválida

En Line 36 y Line 60 la expresión @authorizationServiceImpl.canAccessResource(#id, authentication) hace referencia a #id, pero ni createTrainer(...) ni getAllTrainers() declaran ese parámetro. Spring evalúa la expresión antes de invocar el método y lanzará SpelEvaluationException, dejando los endpoints inutilizables. Ajusta la anotación para usar únicamente los argumentos disponibles (por ejemplo, una variante que reciba solo authentication) o introduce el parámetro necesario.

🧹 Nitpick comments (6)
pom.xml (1)

16-17: Alinea el artefacto con el nuevo nombre del microservicio

Has renombrado <name> y <description> a msvc-classes, pero <artifactId> continúa siendo msvc-members. El paquete generado seguirá llamándose msvc-members-0.0.1-SNAPSHOT.jar, lo que genera divergencia frente al objetivo de renombrar el servicio y puede confundir a los pipelines/deploys. Sugiero actualizar también el artifactId (y, si aplica, ajustar la descripción) para mantener coherencia.

-    <artifactId>msvc-members</artifactId>
+    <artifactId>msvc-classes</artifactId>
...
-    <description>msvc-classes</description>
+    <description>Microservicio de clases</description>
src/main/java/com/classes/services/TrainerService.java (1)

25-28: Eliminar líneas vacías innecesarias.

Las líneas vacías al final del archivo son innecesarias y deben eliminarse para mantener la consistencia del código.

-


-

 }
src/main/java/com/classes/repositories/TrainerRepository.java (2)

13-15: Eliminar líneas vacías innecesarias.

Las líneas vacías al final de la interfaz son innecesarias y deben eliminarse.

 public interface TrainerRepository extends JpaRepository<TrainerEntity, UUID> {
-

-

 }

7-7: Eliminar import no utilizado java.util.Optional. Este import no se emplea en la interfaz y puede eliminarse para mantener el código limpio.

src/main/java/com/classes/dtos/Class/ClassRequest.java (1)

3-25: Agrega validaciones de Bean Validation.

Sin restricciones Bean Validation, una solicitud con datos nulos o valores negativos pasará el @Valid hasta la capa de persistencia y terminará en errores menos controlados (por ejemplo, DataIntegrityViolationException). Anota los campos obligatorios y numéricos con las constraints correspondientes para cortar esos casos en el DTO.

 import com.fasterxml.jackson.annotation.JsonFormat;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
@@
-    private String className;
-    private UUID locationId;
-    private UUID trainerId;
+    @NotBlank
+    private String className;
+    @NotNull
+    private UUID locationId;
+    @NotNull
+    private UUID trainerId;
     @JsonFormat(pattern = "dd-MM-yyyy")
-    private LocalDate classDate;
-    private int duration;
-    private int maxCapacity;
-    private LocalTime startTime;
+    @NotNull
+    private LocalDate classDate;
+    @Positive
+    private int duration;
+    @Positive
+    private int maxCapacity;
+    @NotNull
+    private LocalTime startTime;
     private boolean active;
     private String description;
src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java (1)

26-30: Considerar logging para decisiones de autorización.

El método canAccessResource implementa lógica de autorización crítica pero no registra las decisiones. Agregar logs puede ayudar en auditorías de seguridad y depuración de problemas de acceso.

Considera agregar logging como se muestra:

     @Override
     // Verifica si el usuario es admin o dueño del recurso
     public boolean canAccessResource(UUID ownerId, Authentication authentication) {
-        return hasRole(authentication, "ROLE_ADMIN") || ownerId.equals(getUserId(authentication));
+        boolean isAdmin = hasRole(authentication, "ROLE_ADMIN");
+        boolean isOwner = ownerId.equals(getUserId(authentication));
+        boolean hasAccess = isAdmin || isOwner;
+        
+        log.debug("Access check for resource {}: isAdmin={}, isOwner={}, granted={}", 
+                  ownerId, isAdmin, isOwner, hasAccess);
+        
+        return hasAccess;
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7789c47 and 5b58c8f.

📒 Files selected for processing (39)
  • pom.xml (1 hunks)
  • src/main/java/com/classes/config/SecurityConfig.java (1 hunks)
  • src/main/java/com/classes/controllers/ClassController.java (1 hunks)
  • src/main/java/com/classes/controllers/HelloController.java (1 hunks)
  • src/main/java/com/classes/controllers/LocationController.java (2 hunks)
  • src/main/java/com/classes/controllers/TrainerController.java (4 hunks)
  • src/main/java/com/classes/dtos/Class/ClassRequest.java (1 hunks)
  • src/main/java/com/classes/dtos/Class/ClassResponse.java (1 hunks)
  • src/main/java/com/classes/dtos/Location/LocationRequest.java (1 hunks)
  • src/main/java/com/classes/dtos/Location/LocationResponse.java (1 hunks)
  • src/main/java/com/classes/dtos/Trainer/FileResponseDTO.java (1 hunks)
  • src/main/java/com/classes/dtos/Trainer/TrainerDTO.java (3 hunks)
  • src/main/java/com/classes/entities/ClassEntity.java (2 hunks)
  • src/main/java/com/classes/entities/TrainerEntity.java (2 hunks)
  • src/main/java/com/classes/enums/ContractType.java (1 hunks)
  • src/main/java/com/classes/enums/DayAvailability.java (1 hunks)
  • src/main/java/com/classes/enums/Gender.java (1 hunks)
  • src/main/java/com/classes/enums/TrainerStatus.java (1 hunks)
  • src/main/java/com/classes/exceptions/GlobalExceptionController.java (2 hunks)
  • src/main/java/com/classes/mappers/ClassMapper.java (1 hunks)
  • src/main/java/com/classes/mappers/LocationMapper.java (2 hunks)
  • src/main/java/com/classes/mappers/TrainerMapper.java (1 hunks)
  • src/main/java/com/classes/repositories/ClassRepository.java (1 hunks)
  • src/main/java/com/classes/repositories/LocationRepository.java (1 hunks)
  • src/main/java/com/classes/repositories/TrainerRepository.java (1 hunks)
  • src/main/java/com/classes/services/AuthorizationService.java (1 hunks)
  • src/main/java/com/classes/services/AzureService.java (1 hunks)
  • src/main/java/com/classes/services/ClassService.java (1 hunks)
  • src/main/java/com/classes/services/CloudinaryService.java (1 hunks)
  • src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java (1 hunks)
  • src/main/java/com/classes/services/Impl/AzureServiceImpl.java (1 hunks)
  • src/main/java/com/classes/services/Impl/ClassServiceImpl.java (1 hunks)
  • src/main/java/com/classes/services/Impl/CloudinaryServiceImpl.java (1 hunks)
  • src/main/java/com/classes/services/Impl/LocationServiceImpl.java (2 hunks)
  • src/main/java/com/classes/services/Impl/TrainerServiceImpl.java (4 hunks)
  • src/main/java/com/classes/services/LocationService.java (1 hunks)
  • src/main/java/com/classes/services/TrainerService.java (2 hunks)
  • src/main/java/com/classes/services/TrainerServiceImpl.java (0 hunks)
  • src/main/resources/bootstrap.yml (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/com/classes/services/TrainerServiceImpl.java
🧰 Additional context used
🧬 Code graph analysis (8)
src/main/java/com/classes/dtos/Location/LocationRequest.java (1)
src/main/java/com/classes/dtos/Location/LocationResponse.java (1)
  • Data (7-14)
src/main/java/com/classes/dtos/Class/ClassResponse.java (1)
src/main/java/com/classes/dtos/Class/ClassRequest.java (1)
  • Data (12-26)
src/main/java/com/classes/dtos/Location/LocationResponse.java (1)
src/main/java/com/classes/dtos/Location/LocationRequest.java (1)
  • Data (5-11)
src/main/java/com/classes/controllers/HelloController.java (2)
src/main/java/com/classes/controllers/LocationController.java (1)
  • RestController (16-65)
src/main/java/com/classes/controllers/TrainerController.java (1)
  • RestController (26-98)
src/main/java/com/classes/controllers/ClassController.java (2)
src/main/java/com/classes/controllers/LocationController.java (1)
  • RestController (16-65)
src/main/java/com/classes/controllers/TrainerController.java (1)
  • RestController (26-98)
src/main/java/com/classes/dtos/Class/ClassRequest.java (1)
src/main/java/com/classes/dtos/Class/ClassResponse.java (1)
  • Data (13-28)
src/main/java/com/classes/services/Impl/ClassServiceImpl.java (2)
src/main/java/com/classes/services/Impl/LocationServiceImpl.java (1)
  • Service (18-81)
src/main/java/com/classes/services/Impl/TrainerServiceImpl.java (1)
  • Service (23-119)
src/main/java/com/classes/controllers/TrainerController.java (1)
src/main/java/com/classes/exceptions/GlobalExceptionController.java (1)
  • Slf4j (22-137)
🔇 Additional comments (23)
src/main/java/com/classes/services/Impl/CloudinaryServiceImpl.java (1)

3-3: LGTM: Import actualizado correctamente.

La actualización del import para FileResponseDTO es consistente con la reorganización de paquetes. La implementación del servicio permanece sin cambios y funcional.

src/main/java/com/classes/services/CloudinaryService.java (1)

3-3: LGTM: Interfaz actualizada correctamente.

La actualización del import de FileResponseDTO mantiene la consistencia con la reorganización de paquetes. El contrato de la interfaz permanece sin cambios.

src/main/java/com/classes/mappers/TrainerMapper.java (1)

4-4: LGTM: Import del mapper actualizado correctamente.

La actualización del import para TrainerDTO al paquete com.classes.dtos.Trainer es consistente con la reorganización de DTOs. Los métodos del mapper permanecen sin cambios y funcionales.

src/main/java/com/classes/services/Impl/AzureServiceImpl.java (1)

8-8: LGTM: Import actualizado correctamente.

La actualización del import para FileResponseDTO al paquete com.classes.dtos.Trainer es consistente con la reorganización de paquetes. La implementación del servicio de Azure permanece sin cambios y funcional.

src/main/java/com/classes/enums/ContractType.java (1)

1-1: LGTM: Convención de nombres de paquetes corregida.

El cambio de com.classes.Enums a com.classes.enums (minúsculas) sigue las convenciones de nomenclatura de Java y es consistente con la migración de otros enums en el proyecto.

src/main/java/com/classes/enums/DayAvailability.java (1)

1-1: LGTM: Convención de nombres de paquetes corregida. No se encontraron referencias a com.classes.Enums.

src/main/java/com/classes/dtos/Location/LocationRequest.java (1)

1-11: LGTM: Patrón Request/Response implementado correctamente. Renombramiento de LocationDTO a LocationRequest y reubicación al paquete com.classes.dtos.Location son consistentes. No se encontraron referencias al DTO antiguo en el código.

src/main/java/com/classes/dtos/Trainer/FileResponseDTO.java (1)

1-1: Reorganización de paquetes consistente
No se encontraron referencias al paquete antiguo com.classes.dtos.FileResponseDTO; el movimiento es seguro.

src/main/java/com/classes/enums/Gender.java (1)

1-1: ¡Buen cambio!

La corrección del nombre del paquete de com.classes.Enums a com.classes.enums sigue las convenciones de nomenclatura de Java (nombres de paquetes en minúsculas).

src/main/java/com/classes/enums/TrainerStatus.java (1)

1-1: ¡Buen cambio!

La corrección del nombre del paquete sigue las convenciones de nomenclatura de Java.

src/main/java/com/classes/services/AzureService.java (1)

3-3: ¡LGTM!

La actualización de la ruta del import alinea con la reorganización de DTOs en el proyecto.

src/main/java/com/classes/config/SecurityConfig.java (1)

33-34: Verificar la necesidad del path raíz "/".

La adición del path /test/** es correcta y alinea con los cambios en HelloController. Sin embargo, el path "/" en la línea 33 puede ser redundante. Verifique si el endpoint raíz debe ser público o si puede eliminarse.

src/main/java/com/classes/mappers/LocationMapper.java (2)

4-5: ¡Excelente refactorización!

La separación de LocationDTO en LocationRequest y LocationResponse sigue el patrón de diseño Request/Response, mejorando la separación de responsabilidades y la claridad de la API.


16-23: ¡LGTM!

Los métodos de mapeo están correctamente implementados:

  • toResponse() para convertir entidad a respuesta
  • toEntity() para convertir request a entidad
  • updateFromRequest() con la estrategia apropiada de ignorar valores nulos

La nomenclatura es clara y consistente.

src/main/java/com/classes/controllers/HelloController.java (1)

9-13: ¡Mejora adecuada!

Los cambios implementados son positivos:

  • El uso de ResponseEntity<String> es más apropiado que retornar un String directo, permitiendo un mejor control de las respuestas HTTP.
  • El cambio de path a /test/saludo está correctamente alineado con los cambios en SecurityConfig.java que permiten acceso público a /test/**.
  • La adición de @GetMapping hace el código más explícito y legible.
src/main/java/com/classes/services/AuthorizationService.java (1)

7-15: LGTM: Diseño de interfaz bien estructurado.

La interfaz AuthorizationService define claramente los contratos de autorización necesarios con métodos bien nombrados que cubren escenarios comunes (verificación de roles, extracción de ID de usuario, y control de acceso basado en propiedad de recursos).

src/main/java/com/classes/dtos/Trainer/TrainerDTO.java (2)

3-6: LGTM: Imports actualizados correctamente.

El cambio de com.classes.Enums.* a com.classes.enums.* sigue las convenciones de nomenclatura de Java donde los paquetes deben estar en minúsculas.


21-21: Verificar compatibilidad de clientes con nuevo formato de fecha

No se encontraron otros usos de @JsonFormat(pattern="yyyy-MM-dd") en los DTOs, pero cambiar a dd-MM-yyyy puede romper clientes que esperan ISO. Revisa pruebas de integración y coordina con consumidores del API para confirmar que el nuevo formato no afecta la compatibilidad.

src/main/java/com/classes/dtos/Class/ClassResponse.java (1)

13-28: LGTM: DTO bien estructurado.

ClassResponse está correctamente definido con:

  • Anotaciones Lombok apropiadas para reducir boilerplate
  • Formato de fecha consistente ("dd-MM-yyyy") con otros DTOs del PR
  • Campos descriptivos que incluyen nombres legibles (locationName, trainerName) en lugar de IDs anidados
src/main/java/com/classes/controllers/ClassController.java (1)

40-45: LGTM: Autorización correcta en endpoints con ID.

Los métodos updateClass y deleteClass utilizan correctamente @PreAuthorize con la variable #id que existe como @PathVariable.

Also applies to: 47-52

src/main/java/com/classes/mappers/ClassMapper.java (1)

18-32: LGTM: Mapper bien diseñado con MapStruct.

El ClassMapper está correctamente implementado con:

  • Mapeos explícitos que ignoran campos que se setean en el servicio (location, trainer)
  • Mapeo de nombres legibles desde relaciones (location.name → locationName)
  • Estrategia IGNORE para valores nulos en actualizaciones, evitando sobrescribir campos no enviados
  • Comentarios útiles que documentan el propósito de cada método
src/main/java/com/classes/repositories/LocationRepository.java (1)

16-24: LGTM: Métodos de consulta con paginación bien implementados.

Los métodos agregados siguen las convenciones de Spring Data JPA y proporcionan capacidades útiles de filtrado:

  • Búsqueda case-insensitive por nombre
  • Filtros por estado activo/inactivo
  • Combinación de búsqueda y filtro de estado
  • Soporte completo de paginación con Pageable
src/main/java/com/classes/services/LocationService.java (1)

11-21: LGTM: Interfaz actualizada con patrón Request/Response.

La interfaz LocationService se actualizó correctamente para usar DTOs separados:

  • LocationRequest para operaciones de entrada (create/update)
  • LocationResponse para operaciones de salida
  • Soporte de paginación con Page<LocationResponse>
  • Parámetros de filtrado bien definidos (search, active)

Este patrón mejora la separación de responsabilidades y permite evolucionar las APIs de entrada/salida de forma independiente.

Comment thread src/main/java/com/classes/controllers/ClassController.java
Comment thread src/main/java/com/classes/controllers/ClassController.java
Comment on lines +24 to +42
@PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)")
@PostMapping
public ResponseEntity<LocationDTO> create(@RequestBody LocationDTO dto) {
LocationDTO created = locationService.create(dto);
public ResponseEntity<LocationResponse> create(@RequestBody LocationRequest request) {
LocationResponse created = locationService.create(request);
return new ResponseEntity<>(created, HttpStatus.CREATED);
}

@PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)")
@GetMapping
public ResponseEntity<Page<LocationDTO>> findAll(
public ResponseEntity<Page<LocationResponse>> findAll(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String search,
@RequestParam(required = false) Boolean active
) {
Page<LocationDTO> locations = locationService.findAll(page, size);
Page<LocationResponse> locations = locationService.findAll(page, size, search, active);
return ResponseEntity.ok(locations);
}

@PreAuthorize("@authorizationServiceImpl.canAccessResource(#id,authentication)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Corregir expresión @PreAuthorize inválida

En Line 24 y Line 31 la expresión @authorizationServiceImpl.canAccessResource(#id,authentication) usa #id, pero create(...) y findAll(...) no exponen ese parámetro. Spring evaluará el SpEL y lanzará SpelEvaluationException, bloqueando cualquier petición a estos endpoints. Ajusta la condición para basarte solo en los argumentos realmente presentes o proporciona el parámetro requerido.

🤖 Prompt for AI Agents
En src/main/java/com/classes/controllers/LocationController.java lines 24-42:
las anotaciones @PreAuthorize referencian #id pero los métodos create(...) y
findAll(...) no tienen ese parámetro, lo que causará SpelEvaluationException;
remueve la referencia a #id y adapta la expresión para usar solo argumentos
presentes (por ejemplo pasar solo authentication:
@PreAuthorize("@authorizationServiceImpl.canAccessResource(authentication)") o
cambiar a una comprobación equivalente que use roles/privilegios disponibles), o
alternativamente expone el parámetro requerido en la firma del método si
realmente debe evaluarse por id.

Comment on lines +126 to +134
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleEntityNotFoundException(EntityNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(
"ENTITY_NOT_FOUND",
"No se encontró la entidad solicitada",
List.of(ex.getMessage())
);
log.warn("Entity not found: {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Evita NullPointerException al componer el detalle.

List.of(ex.getMessage()) falla con NullPointerException cuando la excepción no trae mensaje (caso habitual al lanzar new EntityNotFoundException()). El handler terminaría devolviendo 500 y perderíamos el control del error.

-        ErrorResponse errorResponse = new ErrorResponse(
-                "ENTITY_NOT_FOUND",
-                "No se encontró la entidad solicitada",
-                List.of(ex.getMessage())
-        );
+        String detail = ex.getMessage() != null ? ex.getMessage() : "Entidad no encontrada";
+        ErrorResponse errorResponse = new ErrorResponse(
+                "ENTITY_NOT_FOUND",
+                "No se encontró la entidad solicitada",
+                Collections.singletonList(detail)
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleEntityNotFoundException(EntityNotFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse(
"ENTITY_NOT_FOUND",
"No se encontró la entidad solicitada",
List.of(ex.getMessage())
);
log.warn("Entity not found: {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<ErrorResponse> handleEntityNotFoundException(EntityNotFoundException ex) {
String detail = ex.getMessage() != null ? ex.getMessage() : "Entidad no encontrada";
ErrorResponse errorResponse = new ErrorResponse(
"ENTITY_NOT_FOUND",
"No se encontró la entidad solicitada",
Collections.singletonList(detail)
);
log.warn("Entity not found: {}", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
🤖 Prompt for AI Agents
In src/main/java/com/classes/exceptions/GlobalExceptionController.java around
lines 126 to 134, the call List.of(ex.getMessage()) can throw a
NullPointerException when ex.getMessage() is null; change the construction of
the details list to handle null safely (e.g., use ex.getMessage() != null ?
List.of(ex.getMessage()) : List.of()) so the handler always returns a valid
ErrorResponse and a 404, and keep the existing log call.

Comment on lines +20 to +24
@Override
public UUID getUserId(Authentication authentication) {
String userIdStr = ((JwtAuthenticationToken) authentication).getToken().getClaim("user_id");
return UUID.fromString(userIdStr);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Agregar validaciones de null y manejo de excepciones.

El método getUserId tiene varios problemas de seguridad en tiempo de ejecución:

  1. Cast sin verificación de tipo (línea 22): Si authentication no es una instancia de JwtAuthenticationToken, se lanzará ClassCastException
  2. Falta validación de null: Si el claim "user_id" es null, UUID.fromString lanzará NullPointerException
  3. Formato inválido: Si el claim no es un UUID válido, se lanzará IllegalArgumentException

Aplica este diff para agregar las validaciones necesarias:

     @Override
     public UUID getUserId(Authentication authentication) {
+        if (!(authentication instanceof JwtAuthenticationToken)) {
+            throw new IllegalArgumentException("Authentication must be a JwtAuthenticationToken");
+        }
         String userIdStr = ((JwtAuthenticationToken) authentication).getToken().getClaim("user_id");
+        if (userIdStr == null) {
+            throw new IllegalStateException("user_id claim not found in JWT token");
+        }
+        try {
-        return UUID.fromString(userIdStr);
+            return UUID.fromString(userIdStr);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException("Invalid UUID format for user_id: " + userIdStr, e);
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public UUID getUserId(Authentication authentication) {
String userIdStr = ((JwtAuthenticationToken) authentication).getToken().getClaim("user_id");
return UUID.fromString(userIdStr);
}
@Override
public UUID getUserId(Authentication authentication) {
if (!(authentication instanceof JwtAuthenticationToken)) {
throw new IllegalArgumentException("Authentication must be a JwtAuthenticationToken");
}
String userIdStr = ((JwtAuthenticationToken) authentication).getToken().getClaim("user_id");
if (userIdStr == null) {
throw new IllegalStateException("user_id claim not found in JWT token");
}
try {
return UUID.fromString(userIdStr);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid UUID format for user_id: " + userIdStr, e);
}
}
🤖 Prompt for AI Agents
In src/main/java/com/classes/services/Impl/AuthorizationServiceImpl.java around
lines 20 to 24, the getUserId method currently casts without checking and
assumes the "user_id" claim is non-null and a valid UUID; change it to first
verify authentication is an instance of JwtAuthenticationToken and throw an
appropriate exception if not, retrieve the "user_id" claim safely as a String
and validate it's not null or blank, then parse UUID inside a try-catch to catch
IllegalArgumentException and rethrow a clear, informative exception (or return
an Optional/nullable value per project conventions) so ClassCastException,
NullPointerException and invalid-format exceptions are prevented and errors
include context for debugging.

Comment on lines +37 to +41
LocationEntity entity = repository.findById(id)
.orElseThrow(() -> new RuntimeException("Location not found"));
mapper.updateFromRequest(request, entity);
LocationEntity updated = repository.save(entity);
return mapper.toResponse(updated);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Usa una excepción específica para el 404

RuntimeException("Location not found") terminará en un 500 si no existe un handler dedicado, lo que rompe el contrato REST al no devolver 404. Propón usar una excepción de dominio (p.ej. ResourceNotFoundException) o ResponseStatusException(HttpStatus.NOT_FOUND); así el controlador responderá con el código correcto y un mensaje consistente.

🤖 Prompt for AI Agents
In src/main/java/com/classes/services/Impl/LocationServiceImpl.java around lines
37 to 41, the code throws a generic RuntimeException when a Location isn't
found—causing a 500 instead of a 404; replace the RuntimeException with a
domain-specific exception (e.g., throw new ResourceNotFoundException("Location
not found", id)) or throw new ResponseStatusException(HttpStatus.NOT_FOUND,
"Location not found") via the orElseThrow so the REST layer returns 404, and if
you choose ResourceNotFoundException ensure that exception class exists (or
create it) and is mapped by a @ControllerAdvice or appropriate handler so
responses have the correct status and message.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/main/java/com/classes/dtos/Class/ClassResponse.java (1)

10-10: Elimina la importación no utilizada.

La importación de LocalTime no se utiliza en esta clase, ya que el campo schedule es de tipo String.

Aplica este diff para eliminar la importación:

-import java.time.LocalTime;
src/main/java/com/classes/mappers/ClassMapper.java (1)

25-25: Formatea explícitamente los valores de tiempo en el schedule.

La concatenación directa de objetos LocalTime mediante el operador + en la expresión depende del método toString() de LocalTime, que devuelve el formato ISO-8601 (por ejemplo, "09:30:00"). Aunque funcional, esto puede ser inconsistente con otros formatos de fecha/hora utilizados en la aplicación (como el patrón "dd-MM-yyyy" en classDate).

Considera formatear explícitamente los tiempos para mayor claridad y control:

-    @Mapping(target = "schedule", expression = "java(entity.getStartTime() + \" - \" + entity.getEndTime())")
+    @Mapping(target = "schedule", expression = "java(entity.getStartTime().format(java.time.format.DateTimeFormatter.ofPattern(\"HH:mm\")) + \" - \" + entity.getEndTime().format(java.time.format.DateTimeFormatter.ofPattern(\"HH:mm\")))")

O, alternativamente, crea un método auxiliar para mejorar la legibilidad:

default String formatSchedule(LocalTime startTime, LocalTime endTime) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
    return startTime.format(formatter) + " - " + endTime.format(formatter);
}

Y actualiza el mapping:

-    @Mapping(target = "schedule", expression = "java(entity.getStartTime() + \" - \" + entity.getEndTime())")
+    @Mapping(target = "schedule", expression = "java(formatSchedule(entity.getStartTime(), entity.getEndTime()))")
src/main/java/com/classes/entities/ClassEntity.java (2)

34-40: Especifica la estrategia de carga para evitar problemas de rendimiento N+1.

Las relaciones @ManyToOne para location y trainer no especifican una estrategia de carga (fetch), por lo que se utiliza el valor predeterminado FetchType.EAGER. Esto puede causar consultas N+1 y problemas de rendimiento cuando se cargan múltiples instancias de ClassEntity.

Considera especificar explícitamente FetchType.LAZY para mejorar el rendimiento:

-    @ManyToOne
+    @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "location_id")
     private LocationEntity location;

-    @ManyToOne
+    @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "trainer_id")
     private TrainerEntity trainer;

Si necesitas cargar estas relaciones en casos específicos, puedes usar @EntityGraph o consultas con JOIN FETCH en el repositorio.


26-31: Considera añadir validaciones JPA en los campos de la entidad.

Los campos como duration, maxCapacity, className, etc., no tienen anotaciones de validación (por ejemplo, @NotNull, @Positive, @Size). Aunque las validaciones pueden estar presentes en los DTOs, añadirlas también en la entidad proporciona una capa adicional de protección contra datos inválidos a nivel de persistencia.

Ejemplo de validaciones sugeridas:

+    @NotBlank
     private String className;
+    @Positive
     private int duration;
+    @Positive
     private int maxCapacity;
+    @NotNull
     private LocalTime startTime;
+    @NotNull
     private LocalTime endTime;

Asegúrate de importar jakarta.validation.constraints.* si decides implementar estas validaciones.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b58c8f and 5d7f115.

📒 Files selected for processing (4)
  • src/main/java/com/classes/dtos/Class/ClassRequest.java (1 hunks)
  • src/main/java/com/classes/dtos/Class/ClassResponse.java (1 hunks)
  • src/main/java/com/classes/entities/ClassEntity.java (2 hunks)
  • src/main/java/com/classes/mappers/ClassMapper.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/classes/dtos/Class/ClassRequest.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/classes/dtos/Class/ClassResponse.java (1)
src/main/java/com/classes/dtos/Class/ClassRequest.java (1)
  • Data (12-27)

Comment on lines +23 to +25
private String name;

private String className;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Aclara el propósito de los campos name y className.

La entidad tiene tanto un campo name (línea 23) como un campo className (línea 25), lo que puede resultar confuso. El resumen indica que el campo title fue renombrado a name, pero también se añadió un nuevo campo className. Esto podría indicar:

  • Una refactorización incompleta donde uno de los campos debe ser eliminado, o
  • Un diseño intencional donde ambos campos tienen propósitos diferentes (por ejemplo, name para uso interno y className para visualización).

Verifica si ambos campos son necesarios y, de ser así, considera añadir documentación JavaDoc para aclarar la diferencia entre ellos. Si uno es redundante, elimínalo.

Ejecuta el siguiente script para verificar el uso de ambos campos en el código:


🏁 Script executed:

#!/bin/bash
# Buscar usos del campo "name" en ClassEntity
echo "=== Usos del campo 'name' en ClassEntity ==="
rg -nP '\bname\b' --type=java -C3 -g '!**/test/**'

echo ""
echo "=== Usos del campo 'className' en ClassEntity ==="
rg -nP '\bclassName\b' --type=java -C3 -g '!**/test/**'

Length of output: 13394


Eliminar campo redundante name en ClassEntity o clarificar su propósito
El campo name en ClassEntity no se usa en ClassRequest, ClassResponse ni en repositorios; elimina name o añade JavaDoc que diferencie claramente su función de className.

🤖 Prompt for AI Agents
In src/main/java/com/classes/entities/ClassEntity.java around lines 23-25, the
private String name field is redundant with className; either remove the name
field (delete its declaration, getters/setters/usages, update constructors,
equals/hashCode, DTO mappings, and any JPA/DB mappings) or keep it but add clear
JavaDoc above the field explaining how it differs from className and rename it
to a more descriptive identifier if needed; update ClassRequest/ClassResponse,
repository queries, and tests accordingly so there is no ambiguity or unused
field left behind.

@Raydberg Raydberg closed this Oct 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

corregir errores en microservicio de clases

2 participants